﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using System.ServiceModel;
using Anagramme.Contracts;
using System.Threading.Tasks;
using System.Web;
using Anagramme.Contracts.Resources;

namespace Anagramme.Server.Service
{
    /// <summary>
    /// Service de lobby
    /// </summary>
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
    public sealed class LobbyService : ServiceBase<ILobbyServiceCallback>, ILobbyService
    {
        #region Static Fields
        private static int _playerIdCounter;
        #endregion

        #region ILobbyService Implementation
        /// <summary>
        /// Envoie d'une demande pour changer le mode de jeu
        /// </summary>
        /// <param name="gameMode">Nouveau mode de jeu</param>
        /// <remarks>Nécessite d'être le maitre du lobby</remarks>
        public void RequestLobbyChangeGameMode(GameModes gameMode)
        {
            Context.LobbyInfo.GameMode = gameMode;
            foreach (ILobbyServiceCallback callback in base.OthersClientCallbacks)
            {
                callback.ResponseLobbyChangeGameMode(gameMode);
            }
        }

        /// <summary>
        /// Envoie d'une demande pour changer la longueur maximale des mots
        /// </summary>
        /// <param name="length">Nouvelle longueur maximale des mots</param>
        /// <remarks>Nécessite d'être le maitre du lobby</remarks>
        public void RequestLobbyChangeMaxWordsLength(int length)
        {
            Context.LobbyInfo.MaxWordsLength = length;
            foreach (ILobbyServiceCallback callback in base.OthersClientCallbacks)
            {
                callback.ResponseLobbyChangeMaxWordsLength(length);
            }
        }

        /// <summary>
        /// Envoie d'une demande pour changer la longueur minimale des mots
        /// </summary>
        /// <param name="length">Nouvelle longueur minimale des mots</param>
        /// <remarks>Nécessite d'être le maitre du lobby</remarks>
        public void RequestLobbyChangeMinWordsLength(int length)
        {
            Context.LobbyInfo.MinWordsLength = length;
            foreach (ILobbyServiceCallback callback in base.OthersClientCallbacks)
            {
                callback.ResponseLobbyChangeMinWordsLength(length);
            }
        }

        /// <summary>
        /// Envoie d'une demande pour changer le nombre de rounds cible
        /// </summary>
        /// <param name="targetRounds">Nouveau nombre de rounds cible</param>
        /// <remarks>Nécessite d'être le maitre du lobby</remarks>
        public void RequestLobbyChangeTargetRounds(int targetRounds)
        {
            Context.LobbyInfo.TargetRounds = targetRounds;
            foreach (ILobbyServiceCallback callback in base.OthersClientCallbacks)
            {
                callback.ResponseLobbyChangeTargetRounds(targetRounds);
            }
        }

        /// <summary>
        /// Envoie d'une demande pour changer le score cible
        /// </summary>
        /// <param name="targetScore">Nouveau score cible</param>
        /// <remarks>Nécessite d'être le maitre du lobby</remarks>
        public void RequestLobbyChangeTargetScore(int targetScore)
        {
            Context.LobbyInfo.TargetScore = targetScore;
            foreach (ILobbyServiceCallback callback in base.OthersClientCallbacks)
            {
                callback.ResponseLobbyChangeTargetScore(targetScore);
            }
        }

        /// <summary>
        /// Envoie d'une demande d'extinction du serveur
        /// </summary>
        /// <remarks>Nécessite d'être le maitre du lobby</remarks>
        public void RequestLobbyClose()
        {
            foreach (ILobbyServiceCallback callback in base.ClientCallbacks)
            {
                callback.ResponseLobbyDisconnect();
            }

#if DEBUG
            System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcessesByName("server.vshost");
#else
            System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcessesByName("server");
#endif

            foreach (System.Diagnostics.Process process in processes)
            {
                process.Kill();
            }
        }

        /// <summary>
        /// Envoie d'une demande pour récupérer les informations sur le serveur
        /// </summary>
        public void RequestLobbyInfos()
        {
            base.ClientCallback.ResponseLobbyInfos(Context.LobbyInfo);
        }

        /// <summary>
        /// Indique que le statut "Prêt" à été modifié
        /// </summary>
        /// <param name="isReady">Nouveau statut "Prêt"</param>
        /// <remarks>Vaut True si le joueur est prêt, sinon False</remarks>
        public void RequestLobbyIsReadyToPlay(bool isReady)
        {
            Player player = Context.Players.Single(p => p.Token.Equals(base.Token));
            player.IsReadyToPlay = isReady;
            foreach (ILobbyServiceCallback callback in base.ClientCallbacks)
            {
                callback.ResponseLobbyPlayerIsReadyToPlay(player.Id, isReady);
            }
        }

        /// <summary>
        /// Envoie d'une demande pour rejoindre la partie
        /// </summary>
        /// <param name="userName">Nom du joueur</param>
        /// <remarks>Cette méthode est la première à appeler sur le serveur, car elle renvoie le jeton de sécurité nécessaire à tout les autres appels</remarks>
        public void RequestLobbyJoin(string userName)
        {
            if (object.ReferenceEquals(Context.Players, null) || Context.Players.Count.Equals(0))
            {
                Context.State = ServerState.InLobby;

                Context.Players = new List<Player>();

                Context.LobbyInfo = new LobbyInfo();
                Context.LobbyInfo.MinWordsLength = 5;
                Context.LobbyInfo.MaxWordsLength = 8;
#if DEBUG
                Context.LobbyInfo.TargetRounds = 1;
#else
                Context.LobbyInfo.TargetRounds = 5;
#endif
                Context.LobbyInfo.TargetScore = 20;
                Context.LobbyInfo.GameMode = GameModes.RoundMode;

                Context.RedTeam = new Team();
                Context.RedTeam.Id = 0;
                Context.RedTeam.Name = Resources.ServiceStrings.RED_TEAM_NAME;

                Context.BlueTeam = new Team();
                Context.BlueTeam.Id = 1;
                Context.BlueTeam.Name = Resources.ServiceStrings.BLUE_TEAM_NAME;
            }

            if (Context.Players.Count < 2)
            {
                Guid token = Guid.NewGuid();
                base.LogOn(token);
                _playerIdCounter++;

                Player player = new Player(base.Token);
                player.Id = _playerIdCounter;
                player.IsReadyToPlay = false;
                player.Name = userName;
                if (Context.Players.Count.Equals(0))
                {
                    player.IsLobbyMaster = true;
                    player.Team = Context.RedTeam;
                }
                else
                {
                    player.Team = Context.BlueTeam;
                }
                Context.Players.Add(player);

                foreach (ILobbyServiceCallback callback in base.OthersClientCallbacks)
                {
                    callback.ResponseLobbyPlayerJoin(player);
                }
                ClientCallback.ResponseLobbyJoin(base.Token);
            }
            else
            {
                this.ClientCallback.ResponseLobbyJoin(Guid.Empty);
            }
        }

        /// <summary>
        /// Indique au serveur que le joueur quitte le lobby
        /// </summary>
        public void RequestLobbyLeave()
        {
            if (!object.ReferenceEquals(base.Player, null))
            {
                foreach (ILobbyServiceCallback callback in base.OthersClientCallbacks)
                {
                    callback.ResponseLobbyPlayerLeave(base.Player.Id);
                }
            }
            base.LogOff();
        }

        /// <summary>
        /// Envoie d'une demande de chargement de la partie
        /// </summary>
        /// <remarks>Cette demande est propagée aux autres joueurs</remarks>
        /// <remarks>Nécessite d'être le maitre du lobby</remarks>
        public void RequestLobbyLoadGame()
        {
            Context.GameInfo = new GameInfo();
            Context.GameInfo.GameMode = Context.LobbyInfo.GameMode;
            Context.GameInfo.CurrentRound = 0;
            Context.GameInfo.Target = Context.LobbyInfo.GameMode == GameModes.RoundMode ?
                Context.LobbyInfo.TargetRounds :
                Context.LobbyInfo.TargetScore;

            foreach (ILobbyServiceCallback callback in base.ClientCallbacks)
            {
                callback.ResponseLobbyLoadGame();
            }
        }

        /// <summary>
        /// Envoie d'une demande pour récupérer les informations sur le joueur
        /// </summary>
        public void RequestLobbyPlayerInfo()
        {
            Player player = Context.Players.Single(p => p.Token.Equals(base.Token));
            base.ClientCallback.ResponseLobbyPlayerInfo(player);
        }

        /// <summary>
        /// Envoie d'une demande pour récupérer la liste des joueurs de la partie
        /// </summary>
        public void RequestLobbyPlayers()
        {
            base.ClientCallback.ResponseLobbyPlayers(Context.Players);
        }
        #endregion

        #region ServiceBase Overrides
        /// <summary>
        /// Méthode soulevée lorsque le callback du client passe à l'état Closing
        /// </summary>
        protected override void OnChannelClosing()
        {
            base.OnChannelClosing();
            if (!object.ReferenceEquals(this.Player, null) && base.Context.State == ServerState.InLobby)
            {
                if (this.Player.IsLobbyMaster)
                {
                    this.RequestLobbyClose();
                }
                else
                {
                    this.RequestLobbyLeave();
                }
            }
        }
        #endregion
    }
}
